﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Natural.Helpers
{
    public sealed class ReflectionHelper
    {
        public static object CreateInstance(string fullName, params object[] objects)
        {
            return CreateInstance(Type.GetType(fullName), objects);
        }
        public static T CreateInstance<T>(string fullName, params object[] objects) where T : class
        {
            return CreateInstance(Type.GetType(fullName), objects) as T;
        }
        public static object CreateInstance(Type type, params object[] objects)
        {
            return Activator.CreateInstance(type, objects);
        }
        public static T CreateInstance<T>()
        {
            return Activator.CreateInstance<T>();
        }
        public static int Implement(Type source, Type baseType)
        {
            return Implement(source, baseType, 0);
        }
        public static int Implement(Type source, Type baseType,int level)
        {
            return baseType.IsInterface ? ImplementInterface(source, baseType, level) : ImplementBase(source, baseType, level);
        }
        public static int ImplementBase(Type source, Type baseType,int level)
        {
            level++;
            if (source == baseType) return level;
            if (source.BaseType != null && source.BaseType.Name == baseType.Name) return level++;
            if (source.BaseType != null) return ImplementBase(source.BaseType, baseType, level);
            return 0;
        }
        public static int ImplementInterface(Type source, Type interfaceType,int level)
        {
            level++;
            foreach (Type item in source.GetInterfaces())
            {
                if (item.Name == interfaceType.Name)
                {
                    if (interfaceType.IsGenericType == false && source.IsGenericType == false) return level;

                    Type[] interfaceGenericArguments = interfaceType.GetGenericArguments();
                    Type[] sourceGenericArguments = item.GetGenericArguments();

                    if (interfaceGenericArguments.Length == sourceGenericArguments.Length)
                    {
                        int i = 0, count = interfaceGenericArguments.Length;
                        for (; i < count; i++)
                        {
                            if (sourceGenericArguments[i].IsGenericParameter)
                            {
                                if (ImplementGeneric(interfaceGenericArguments[i], sourceGenericArguments[i], level)==0)
                                    break;
                            }
                            else
                            {
                                if (Implement(interfaceGenericArguments[i], sourceGenericArguments[i], level)==0)
                                    break;
                            }
                        }
                        if (i == count) return level;
                    }
                }
            }
            foreach (Type child in source.GetInterfaces())
                return ImplementInterface(child, interfaceType, level);
            return 0;
        }
        public static int ImplementGeneric(Type source, Type baseType)
        {
            return ImplementGeneric(source, baseType, 0);
        }
        public static int ImplementGeneric(Type source, Type baseType,int level)
        {
            Type[] constraint = baseType.GetGenericParameterConstraints();
            int i = 0, count = constraint.Length;
            for (; i < count; i++)
                if (Implement(source, constraint[i])==0)
                    break;
            if (i == count) return level;
            return 0;
        }

        
        public static object Invoke(object instance, string method,object[] parameters)
        {
            return Invoke(instance,instance.GetType().GetMethod(method),parameters);
        }
        public static object Invoke(object instance, MethodInfo method, object[] parameters)
        {
            ParameterInfo[] parametersInfo = method.GetParameters();
            object[] _parameters = new object[parametersInfo.Length];
            foreach (System.Reflection.ParameterInfo parameterInfo in parametersInfo)
            {
                int i = parameterInfo.Position;
                _parameters[i] = parameters.Length > i ? parameters[i] : null;
            }
            return method.Invoke(instance, _parameters);
        }
        public static T Attribute<T>(Type entity) where T : Attribute
        {
            Type _type = typeof(T);
            Attribute[] attributes = System.Attribute.GetCustomAttributes(entity);
            return (T)attributes.Where(p => p.GetType().Equals(_type)).FirstOrDefault();
        }


        //public static bool ImplementMethod(Type source, string methodName, Type[] parametersType)
        //{
        //    return GetMethod(source, methodName, parametersType) != null;
        //}
        //public static System.Reflection.MethodInfo GetMethod(Type source, string methodName, Type[] parametersType)
        //{
        //    List<System.Reflection.MethodInfo> methods = source.GetMethods().Where(p => p.Name == methodName).ToList();
        //    if (methods.Count == 0) return null;
        //    if (methods.Count == 1 && (parametersType == null || parametersType.Length == 0)) return methods[0];

        //    foreach (System.Reflection.MethodInfo method in methods)
        //    {
        //        System.Reflection.ParameterInfo[] parametersInfo = method.GetParameters();
        //        if (parametersInfo.Length == parametersType.Length)
        //        {
        //            int i = 0, count = parametersType.Length;
        //            for (; i < count; i++)
        //            {
        //                if (parametersInfo[i].ParameterType.IsGenericParameter)
        //                {
        //                    if (ImplementGeneric(parametersType[i], parametersInfo[i].ParameterType))
        //                        break;
        //                }
        //                else
        //                {
        //                    if (!Implement(parametersType[i], parametersInfo[i].ParameterType))
        //                        break;
        //                }
        //            }
        //            if (i == count) return method;
        //        }
        //    }
        //    return null;
        //}
        public static MethodInfo GetMethod(Type source, string methodName)
        {
            return source.GetMethods().Where(p => p.Name == methodName).FirstOrDefault();
        }
        public static bool IsFinal(List<Type> list, Type baseType)
        {
            foreach (Type item in list)
                if (item != baseType && item.BaseType != null && ImplementBase(item, baseType,0)>0)
                    return false;
            return true;
        }
        public static string FirstNameNoAbstract(Type type)
        {
            while (type.BaseType != null && !type.BaseType.IsAbstract)
                type = type.BaseType;

            int index = type.Name.IndexOf('`');
            if (index == -1) return type.Name;
            return type.Name.Substring(0, index);
        }
        public static string GetInstanciableName(Type type)
        {
            return type.FullName + ", " + type.Assembly.GetName().Name;
        }
        public static string GetName(Type type)
        {
            if (type == typeof(Nullable<bool>)) return "bool";
            if (type == typeof(Nullable<int>)) return "int";
            if (type == typeof(Nullable<decimal>)) return "decimal";
            return type.Name;
        }
        public static string BaseSimpleName(Type type)
        {
            if (type.BaseType != null && !type.BaseType.IsAbstract)
            {
                return BaseSimpleName(type.BaseType);
            }
            else
            {
                int index = type.Name.IndexOf('`');
                if (index == -1) return type.Name;
                return type.Name.Substring(0, index);
            }
        }
        //public static List<Type> GetFinalImplements(Dictionary<string, Type> listSource, Type typeBase, Type genericToImplement)
        //{
        //    return GetFinalImplements(listSource, typeBase, genericToImplement, null);
        //}
        //public static List<Type> GetFinalImplements(Dictionary<string, Type> listSource, Type typeBase, Type genericToImplement, Predicate<Type> predicate)
        //{
        //    List<Type> _implements = GetImplements(listSource, typeBase, genericToImplement, predicate);
        //    List<Type> implements = new List<Type>(_implements.Count);
        //    foreach (Type implement in _implements)
        //        if (ReflectionHelper.IsFinal(_implements, implement))
        //            implements.Add(implement.IsGenericType ? implement.MakeGenericType(new Type[] { typeBase }) : implement);
        //    return implements;
        //}
        //public static List<Type> GetImplements(Dictionary<string, Type> listSource, Type typeBase, Type genericToImplement, Predicate<Type> predicate)
        //{
        //    Type genericAction = genericToImplement.MakeGenericType(typeBase);

        //    return listSource.Where(p => ReflectionHelper.Implement(p.Value, genericAction) && (predicate != null ? predicate(p.Value) : true)).Select(p => p.Value).ToList();
        //}
        public static string GetFullName(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException("type");
            }
            return type.FullName + ", " + type.Assembly.GetName().Name;
        }
        public static bool Contain(Type type, Attribute attribute)
        {
            Attribute[] attributes = System.Attribute.GetCustomAttributes(type);
            if (attributes.Length == 0) return false;
            return attributes.Where(p => p.TypeId.Equals(attribute.TypeId)).Count() > 0;
        }
    }
}
